import { Vector3, Vector2 } from "stl-typescript";
import { featureCollection, polygon, union } from "@turf/turf";
/**
 * @description 计算法向量
 * @param x1
 * @param y1
 * @param z1
 * @param x2
 * @param y2
 * @param z2
 * @param x3
 * @param y3
 * @param z3
 * @param x4
 * @param y4
 * @param z4
 * @returns
 */
export function calculateNormalFromFourPoints(
  x1: number,
  y1: number,
  z1: number,
  x2: number,
  y2: number,
  z2: number,
  x3: number,
  y3: number,
  z3: number,
  x4?: number,
  y4?: number,
  z4?: number
): Vector3 {
  // 计算向量AB和AC
  let AB = new Vector3(x2 - x1, y2 - y1, z2 - z1);
  let BC = new Vector3(x3 - x2, y3 - y2, z3 - z2);

  // 计算叉积
  let normal = AB.cross(BC);
  // 归一化法向量
  const wNor = new Vector3(normal.x, normal.y, normal.z).normalize();
  // 可选：验证第四个点是否共面
  if (x4 !== undefined && y4 !== undefined && z4 !== undefined) {
    let AD = new Vector3(x4 - x1, y4 - y1, z4 - z1);
    let dotProduct = AD.dot(wNor);
    if (Math.abs(dotProduct) > 0.02) {
      console.error("点 D 不在由 A, B, C 定义的平面上");
    }
  }

  // 返回法线向量
  return wNor;
}

export function sumArray(array: number[]) {
  let sum = 0;
  for (let i = 0; i < array.length; i++) {
    sum += array[i];
  }
  return sum;
}

export function calculatePolygonCenter(vertexes: Vector3[]): Vector3 {
  let sumX = 0;
  let sumY = 0;
  let sumZ = 0;
  for (let i = 0; i < vertexes.length; i++) {
    sumX += vertexes[i].x;
    sumY += vertexes[i].y;
    sumZ += vertexes[i].z;
  }
  return new Vector3(
    sumX / vertexes.length,
    sumY / vertexes.length,
    sumZ / vertexes.length
  );
}

export function isPointsOnLine(px, py, x1, y1, x2, y2) {
  // 计算向量AB和向量AP
  const ABx = x2 - x1;
  const ABy = y2 - y1;
  const APx = px - x1;
  const APy = py - y1;

  // 计算向量的叉乘，如果为0，则向量共线
  const crossProduct = ABx * APy - ABy * APx;
  if (Math.abs(crossProduct) !== 0) {
    return false; // 如果叉乘不为0，则点P不在AB线上
  }

  // 计算点P是否在A和B之间
  const dotProduct = ABx * APx + ABy * APy;
  if (dotProduct < 0) {
    return false; // 点P在A的外部
  }

  const squaredLengthAB = ABx * ABx + ABy * ABy;
  if (dotProduct > squaredLengthAB) {
    return false; // 点P在B的外部
  }

  return true; // 点P在AB线段上
}

/**
 * @description 沿着某个方向偏置一定距离
 * @param point {Vector3} - 需要偏置的点
 * @param targetVec {Vector3} - 偏移方向
 * @param dis {number} 偏置的距离
 */
export function offsetByVec3(point: Vector3, targetVec: Vector3, dis: number) {
  // * 计算法线单位向量
  const nv = targetVec.clone().normalize();
  // * 缩放对应的距离
  const dv = nv.multiplyScalar(dis);
  // * 平移
  return point.add(dv);
}

/**
 * @description 判断一个点是否正好在多边形上
 * @param point
 * @param polygon
 * @returns
 */
export function pointOnPolygon(point: number[], polygon: number[][]) {
  for (let i = 0; i < polygon.length; i++) {
    if (
      isPointsOnLine(
        point[0],
        point[1],
        polygon[i][0],
        polygon[i][1],
        polygon[(i + 1) % polygon.length][0],
        polygon[(i + 1) % polygon.length][1]
      )
    ) {
      return true;
    }
  }
  return false;
}

// * 射线法（Ray-casting Algorithm）
// * 射线法的基本思想是从待判断的点向任意方向发射一条射线（通常是水平向右的射线），
// * 然后观察这条射线与多边形边界的交点数量
// * 如果交点数为奇数，则点在多边形内；如果为偶数，则点在多边形外。

/**
 * @description 判断点是否在多边形内 (二维)
 * @param point
 * @param polygon
 * @returns {boolean}
 */
export function isPointInPolygon(point: Vector2, polygon: Vector2[]): boolean {
  let x = point.x,
    y = point.y;
  let inside = false;
  let n = polygon.length;
  let p1x = polygon[0].x,
    p1y = polygon[0].y;
  for (let i = 1; i <= n; i++) {
    let p2x = polygon[i % n].x,
      p2y = polygon[i % n].y;
    if (y > Math.min(p1y, p2y)) {
      if (y <= Math.max(p1y, p2y)) {
        if (x <= Math.max(p1x, p2x)) {
          if (p1y != p2y) {
            let xinters = ((y - p1y) * (p2x - p1x)) / (p2y - p1y) + p1x;
            if (p1x == p2x || x <= xinters) {
              inside = !inside;
            }
          }
        }
      }
    }
    p1x = p2x;
    p1y = p2y;
  }

  return inside;
}

/**
 * @description 判断点是否在多边形内2 (二维) - 入参类型不同
 * @param point
 * @param polygon
 * @returns {boolean}
 */
export function isPointInPolygon2(
  point: number[],
  polygon: number[][]
): boolean {
  let x = point[0],
    y = point[1];
  let inside = false;
  let n = polygon.length;
  let p1x = polygon[0][0],
    p1y = polygon[0][1];
  for (let i = 1; i <= n; i++) {
    let p2x = polygon[i % n][0],
      p2y = polygon[i % n][1];
    if (y > Math.min(p1y, p2y)) {
      if (y <= Math.max(p1y, p2y)) {
        if (x <= Math.max(p1x, p2x)) {
          if (p1y != p2y) {
            let xinters = ((y - p1y) * (p2x - p1x)) / (p2y - p1y) + p1x;
            if (p1x == p2x || x <= xinters) {
              inside = !inside;
            }
          }
        }
      }
    }
    p1x = p2x;
    p1y = p2y;
  }
  if (!inside) {
    inside = pointOnPolygon(point, polygon);
  }
  return inside;
}

export function calculate2DPolygonCenter(vertexes: Vector2[]): Vector2 {
  let sumX = 0;
  let sumY = 0;
  for (let i = 0; i < vertexes.length; i++) {
    sumX += vertexes[i].x;
    sumY += vertexes[i].y;
  }
  return new Vector2(sumX / vertexes.length, sumY / vertexes.length);
}

/**
 * @description 计算两个多边形的并集 (二维) 注意此处需要收尾相连 即数组的第一个点和最后一个点是同一个
 * @param poly1 - 示例 [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]
 * @param poly2 - 示例 [[5, 5], [15, 5], [15, 15], [5, 15], [5, 5]]
 */
export function unionBetweenPolygons(poly1: number[][], poly2: number[][]) {
  const _poly1 = polygon([poly1]);
  const _poly2 = polygon([poly2]);
  return union(featureCollection([_poly1, _poly2])).geometry.coordinates[0];
}

/**
 * @description 判断一个多边形是否在另一个多边形内 (二维)
 * @param points
 * @param polygon
 * @returns 1 - 点全包含 2 - 有点在里面 3 - 没有点在里面
 */
export function cellInPolygonStrict(
  points: number[][],
  polygon: number[][]
): number {
  let r = []
  for (let i = 0; i < points.length; i++) {
    r.push(isPointInPolygon2([points[i][0], points[i][1]], polygon)) 
  }
  // * 全包
  if(r.every((item) => item === true)) {
    return 1
  }else if(r.every((item) => item === false)) {
    return 2
  }
  return 3
}

/**
 * @description 角度转弧度
 * @param degrees 角度
 * @returns 弧度
 */
export function degreesToRadians(degrees: number): number {
  return degrees * (Math.PI / 180);
}

/**
 * @description 弧度转角度
 * @param radians 弧度
 * @returns 角度
 */
export function radiansToDegrees(radians: number): number {
  return radians * (180 / Math.PI);
}

/**
 * 判断两个数组是否有相交的元素 (数组元素可以是基本类型)
 * @param arr1 第一个数组
 * @param arr2 第二个数组
 * @returns 如果有相交元素返回 true，否则返回 false
 */
export function hasArrayIntersection<T>(oarr1: T[], oarr2: T[]): boolean {
  // 优化避免引用类型的问题 释放占用
  let arr1 = [...oarr1];
  let arr2 = [...oarr2];
  // 优化：选择较小的数组来创建 Set 以提高性能
  if (arr1.length > arr2.length) {
    [arr1, arr2] = [arr2, arr1];
  }
  
  // 将较小的数组转换为 Set 提高查找效率
  const set = new Set(arr1);
  
  // 遍历另一个数组，检查是否有元素存在于 Set 中
  for (const item of arr2) {
    if (set.has(item)) {
      return true;
    }
  }
  
  return false;
}